package com.itbac.simglemachinelimiting.aop;

import com.google.common.collect.Maps;
import com.google.common.util.concurrent.RateLimiter;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Map;


/**
 * AOP切面类
 * @author: BacHe
 * @email: 1218585258@qq.com
 * @Date: 2020/8/15 22:44
 */
@Component
@Aspect  //开启AOP
public class BacRateLimiterAspect {

    private Logger logger = LoggerFactory.getLogger(BacRateLimiterAspect.class);

    //根据请求路径配置限流器
    private Map<String, RateLimiter> limiterMap = Maps.newConcurrentMap();

    //针对自定义注解， 环绕增强
    @Around("@annotation(com.itbac.simglemachinelimiting.aop.BacRateLimiter)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attrs.getRequest();
        //获取请求路径地址  /info/getInfo
        String url = request.getRequestURI();

        //获取方法上的自定义注解
        BacRateLimiter bacRateLimiter = getAnnotation(joinPoint);
        if (null != bacRateLimiter) {
            RateLimiter rateLimiter = null;
            if (!limiterMap.containsKey(url)) {
                //创建
                rateLimiter = RateLimiter.create(bacRateLimiter.permitsPerSecond());
                logger.info("创建一个限流器,路径：{},限流速率：{} /秒",url,bacRateLimiter.permitsPerSecond());
                limiterMap.put(url, rateLimiter);
            }else {
                rateLimiter = limiterMap.get(url);
            }
            //尝试获取令牌  true 获取令牌成功， false 获取令牌失败。
            boolean b = rateLimiter.tryAcquire(bacRateLimiter.timeout(), bacRateLimiter.timeunit());
            if (!b) {
                //流量过大,返回限流消息。
                logger.info("流量过大,返回限流消息");
                return bacRateLimiter.msg() + " 时间：" + System.currentTimeMillis();
            }
        }
        //执行原来的逻辑
        return joinPoint.proceed();
    }
    //从切入点获取自定义注解
    private BacRateLimiter getAnnotation(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
        //1.1 从切入点，获取方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //1.2 从切点，获取目标对象的字节码，的方法。参数：（方法名，方法的所有参数）。
        Method method = joinPoint.getTarget().getClass().getMethod(
                signature.getName(),
                signature.getMethod().getParameterTypes());
        //1.3 从方法获取自定义注解。
        BacRateLimiter annotation = method.getAnnotation(BacRateLimiter.class);
        return annotation;
    }
}
